#include <iostream>
#include <list>
#include <unordered_set>
#include <functional>
#include <memory>
#include <unordered_map>
#include <cassert>
#include <unistd.h>

using OnTimerCallback = std::function<void()>;
using ReleaseCallback = std::function<void()>;

/*定时任务类*/
class Timer {
    private:
        uint64_t _timer_id;
        /*定时任务回调函数*/
        OnTimerCallback _timer_callback;
        /*实际释任务类对象实际析构时要执行的操作*/
        ReleaseCallback _release_callback;
    public:
        Timer(uint64_t timer_id): _timer_id(timer_id){}
        /**/
        ~Timer(){ 
            if (_timer_callback) _timer_callback(); 
            if (_release_callback) _release_callback(); 
        }
        uint64_t id();
        void set_on_time_callback(const OnTimerCallback &cb) {_timer_callback = cb; }
        void set_release_callback(const ReleaseCallback &cb) {_release_callback = cb; }
};

class TimerQueue {
    private:
        using WeakTimer = std::weak_ptr<Timer>;
        using PtrTimer = std::shared_ptr<Timer>;
        /*时间轮每个节点都是一个unordered_set，当然这里用数组也可以，
        当时考虑的是为了便于查找定时任务，但是实际使用中好像也没用到...*/
        using Bucket = std::unordered_set<PtrTimer>;
        /*为了提高一个节点的删除与添加的效率，这里使用了链表实现单层时间轮*/
        using BucketList = std::list<Bucket>;
        /*保存所有定时任务对象的weak_ptr，这样才能在不影响shared_ptr计数器的同时，获取shared_ptr*/
        std::unordered_map<uint64_t, WeakTimer> _timers;
        /*时间轮的容量，这个容量其实就是最大的定时时长*/
        int _capacity;
        /*实现单层时间轮的链表，每秒钟删除一个头节点，添加一个尾节点。
        每个节点中都可以添加多个定时任务，删除头节点的同时，其内部的定时任务类对象就会被释放
        则析构的时候就会执行对应的定时任务*/
        BucketList _conns;
    public:
        TimerQueue(int timeout):_capacity(timeout), _conns(timeout){}
        void timer_add(const OnTimerCallback &cb, uint64_t id) {
            /*new一个定时任务对象出来*/
            Timer *timer = new Timer(id);
            /*设置定时任务对象要执行的定时任务--会在对象被析构时执行*/
            timer->set_on_time_callback(cb);
            /*因为当前类成员_timers中保存了一份定时任务对象的weak_ptr，因此希望在析构的同时进行移除*/
            timer->set_release_callback(std::bind(&TimerQueue::erase, this, id));
            /*为定时任务对象创建一个shared_ptr进行管理*/
            PtrTimer pt(timer);
            /*根据定时任务对象的shared_ptr获取其weak_ptr保存起来，以便于二次刷新任务，
            也就是任务需要延迟执行的时候，重新继续添加一个定时任务进去，可以使用相同的shared_ptr计数*/
            _timers[id] = WeakTimer(pt);
            /*当前定时任务的实现其实是一个固定时间的定时任务，因为总是添加任务到链表末尾，也就是capacity秒之后被执行*/
            _conns.back().insert(pt);
        }
        void timer_refresh(uint64_t id) {
            auto it = _timers.find(id);
            /*不可能存在这种情况呀, 添加的定时任务找不着，
            当然这里可以做的灵活一些，如果不存在就添加一个新的定时任务*/
            assert (it != _timers.end());
            _conns.back().insert(PtrTimer(it->second));
        }
        void erase(uint64_t id) {
            auto it = _timers.find(id);
            if (it != _timers.end()) {
                _timers.erase(it);
            }
        }
        bool find(uint64_t id) {
            auto it = _timers.find(id);
            if (it == _timers.end()) {
                return false;
            }
            return true;
        }
        void on_time() {
            /*每次执行on_time都会移除一个头节点，添加一个尾节点，其实就是把任务从后往前顶，
            顶到最前边的任务就是要被执行的任务*/
            if (_conns.size() == _capacity) {
                _conns.pop_front();
            }
            _conns.push_back(Bucket());
        }
};

class TimerTest{
    private:
        int _data;
    public:
        TimerTest(int data):_data(data) { std::cout << "test 构造!\n"; }
        ~TimerTest() {std::cout << "test 析构!\n"; }
};

void del(TimerTest *t) {
    delete t;
}
int main()
{
    TimerQueue tq(5);

    TimerTest *t = new TimerTest(10); 
    int id = 3;
    tq.timer_add(std::bind(del, t), id);

    for (int i = 0; i < 5; i++) {
        sleep(1);
        tq.timer_refresh(id);
        std::cout << "刷新了一下3号定时任务!\n";
        tq.on_time();//每秒调用一次，模拟定时器
    }
    std::cout << "刷新定时任务停止, 5s后释放任务将被执行\n";
    for (int i = 0; i < 5; i++){
        std::cout << "--------------------\n";
        sleep(1);
        tq.on_time();//每秒调用一次，模拟定时器
    }
    std::cout << tq.find(id) << std::endl;

    return 0;
}